package com.geezdata.cps.base.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyStoreException;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.geezdata.cps.base.constant.ShiroConstants;

public class HttpClientUtil {
	private static volatile HttpClientUtil instance = null;
	private CloseableHttpClient httpClient;
	private RequestConfig requestConfig;
	private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
	
	private static final Integer MAX_CONN_TOTAL = 200;
	private static final Integer MAX_CONN_PER_ROUTE = 20;
	
	private HttpClientUtil() {
		HttpClientBuilder customer = HttpClients.custom();
		customer.setMaxConnTotal(MAX_CONN_TOTAL);
		customer.setMaxConnPerRoute(MAX_CONN_PER_ROUTE);
		httpClient = customer.build();
		requestConfig = RequestConfig.DEFAULT;
	}
	
	private static HttpClientUtil getInstance() {
        if (instance == null) {
            synchronized (HttpClientUtil.class) {
                if (instance == null) {
                    instance = new HttpClientUtil();
                }
            }
        }
        return instance;
    }
		
	
	/**
	 * 执行GET请求
	 * 
	 * @param url
	 * @return
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static String get(String url) throws ClientProtocolException, IOException {
		HttpGet httpGet = new HttpGet(url);
		httpGet.setConfig(HttpClientUtil.getInstance().requestConfig);

		CloseableHttpResponse response = null;
		try {
			response = HttpClientUtil.getInstance().httpClient.execute(httpGet);
			if (response.getStatusLine().getStatusCode() == 200) {
				return EntityUtils.toString(response.getEntity(), "UTF-8");
			}
		} finally {
			if (response != null) {
				response.close();
			}
		}
		return null;
	}
	
	
	public static String get(String url, RequestConfig requestCon) throws ClientProtocolException, IOException {
		HttpGet httpGet = new HttpGet(url);
		httpGet.setConfig(requestCon);

		CloseableHttpResponse response = null;
		try {
			response = HttpClientUtil.getInstance().httpClient.execute(httpGet);
			if (response.getStatusLine().getStatusCode() == 200) {
				return EntityUtils.toString(response.getEntity(), "UTF-8");
			}
		} finally {
			if (response != null) {
				response.close();
			}
		}
		return null;
	}

	/**
	 * 带有参数的GET请求
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static String get(String url, Map<String, Object> params , RequestConfig requestCon)
			throws ClientProtocolException, IOException, URISyntaxException {
		URIBuilder uriBuilder = new URIBuilder(url);
		for (String key : params.keySet()) {
			uriBuilder.addParameter(key, params.get(key).toString());
		}
		
		return HttpClientUtil.get(uriBuilder.build().toString(), requestCon);
	}
	
	/**
	 * 带有参数的GET请求
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws URISyntaxException
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static String get(String url, Map<String, Object> params)
			throws ClientProtocolException, IOException, URISyntaxException {
		URIBuilder uriBuilder = new URIBuilder(url);
		for (String key : params.keySet()) {
			uriBuilder.addParameter(key, params.get(key).toString());
		}
		
		return HttpClientUtil.get(uriBuilder.build().toString());
	}

	/**
	 * 执行POST请求
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws IOException
	 */
	public static String post(String url, Map<String, Object> params) throws IOException {
		return HttpClientUtil.post(url, JSON.toJSONString(params), false);
	}
	
	/**
	 * 执行POST请求
	 * 
	 * @param url
	 * @param params
	 * @param params
	 * @return
	 * @throws IOException
	 */
	public static String post(String url, Map<String, Object> params, boolean fakeLogin) throws IOException {
		return HttpClientUtil.post(url, JSON.toJSONString(params), fakeLogin);
	}
	
	public static String post(String url, Map<String, Object> params, RequestConfig requestCon ) throws IOException {
		return HttpClientUtil.post(url, JSON.toJSONString(params), false, requestCon);
	}
	
	/**
	 * 提交json数据
	 * 
	 * @param url
	 * @param json
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static String post(String url, String json, boolean fakeLogin , RequestConfig requestCon) throws ClientProtocolException, IOException {
		HttpPost httpPost = new HttpPost(url);
		if(url.contains("127.0.0.1") || url.contains("geezdata.com")) {
			try {
				if (fakeLogin) {
					logger.info("fake login" + "; url=" + url + "; json=" + json);
					httpPost.setHeader(ShiroConstants.USER_SCHEDULE, ShiroConstants.USER_SCHEDULE);
				} else {
					logger.info("url=" + url + "; json=" + json);
					httpPost.setHeader("Cookie", "JSESSIONID=" + SecurityUtils.getSubject().getSession().getId());
				}
			} catch (UnavailableSecurityManagerException e) {
				logger.error("schedule fake login" + "; url=" + url + "; json=" + json);
				httpPost.setHeader(ShiroConstants.USER_SCHEDULE, ShiroConstants.USER_SCHEDULE);
			}
			
		}
		httpPost.setConfig(requestCon);

		if (json != null) {
			StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
			httpPost.setEntity(stringEntity);
		}

		CloseableHttpResponse response = null;
		try {
			response = HttpClientUtil.getInstance().httpClient.execute(httpPost);
			String res = EntityUtils.toString(response.getEntity(), "UTF-8");
			
			if (response.getStatusLine().getStatusCode() == 200) {
				return res;
			}
				
			logger.error("httpPost error. url: " + url + ", param: " + json + ", res: " + res + ", statusCode: " + response.getStatusLine().getStatusCode());
		} catch(Exception e) {
			logger.error("httpPost error. url: " + url + ", param: " + json, e);
			return null;
		} finally {
			if (response != null) {
				response.close();
			}
		}
		
		return null;
	}

	/**
	 * 提交json数据
	 * 
	 * @param url
	 * @param json
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static String post(String url, String json, boolean fakeLogin) throws ClientProtocolException, IOException {
		HttpPost httpPost = new HttpPost(url);
		if(url.contains("127.0.0.1") || url.contains("geezdata.com")) {
			try {
				if (fakeLogin) {
					logger.info("fake login" + "; url=" + url + "; json=" + json);
					httpPost.setHeader(ShiroConstants.USER_SCHEDULE, ShiroConstants.USER_SCHEDULE);
				} else {
					logger.info("url=" + url + "; json=" + json);
					httpPost.setHeader("Cookie", "JSESSIONID=" + SecurityUtils.getSubject().getSession().getId());
				}
			} catch (UnavailableSecurityManagerException e) {
				logger.error("schedule fake login" + "; url=" + url + "; json=" + json);
				httpPost.setHeader(ShiroConstants.USER_SCHEDULE, ShiroConstants.USER_SCHEDULE);
			}
			
		}
		httpPost.setConfig(HttpClientUtil.getInstance().requestConfig);

		if (json != null) {
			StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
			httpPost.setEntity(stringEntity);
		}

		CloseableHttpResponse response = null;
		try {
			response = HttpClientUtil.getInstance().httpClient.execute(httpPost);
			String res = EntityUtils.toString(response.getEntity(), "UTF-8");
			
			if (response.getStatusLine().getStatusCode() == 200) {
				return res;
			}
				
			logger.error("httpPost error. url: " + url + ", param: " + json + ", res: " + res + ", statusCode: " + response.getStatusLine().getStatusCode());
		} catch(Exception e) {
			logger.error("httpPost error. url: " + url + ", param: " + json, e);
			return null;
		} finally {
			if (response != null) {
				response.close();
			}
		}
		
		return null;
	}
	public static byte[] getByte(String url) throws ClientProtocolException, IOException {
		HttpGet httpGet = new HttpGet(url);
		httpGet.setConfig(HttpClientUtil.getInstance().requestConfig);

		CloseableHttpResponse response = null;
		try {
			response = HttpClientUtil.getInstance().httpClient.execute(httpGet);
			if (response.getStatusLine().getStatusCode() == 200) {
				return EntityUtils.toByteArray(response.getEntity());
			}
		} finally {
			if (response != null) {
				response.close();
			}
		}
		return null;
	}
	public static byte[] post(String url, String json) {
		HttpPost httpPost = new HttpPost(url);
		httpPost.setConfig(HttpClientUtil.getInstance().requestConfig);

		if (json != null) {
			StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
			httpPost.setEntity(stringEntity);
		}

		CloseableHttpResponse response = null;
		try {
			response = HttpClientUtil.getInstance().httpClient.execute(httpPost);
			return  EntityUtils.toByteArray(response.getEntity());
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}
	
	public static String postFormData(String postUrl, File file) {
		HttpClient httpClient = HttpClients.createDefault();
		if (file == null || !file.exists()) {
		    try {
				throw new FileNotFoundException();
			} catch (FileNotFoundException e) {
				logger.error(e.getMessage(), e);
			}
		}
		MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
		multipartEntityBuilder
		        .addBinaryBody("media", file)
		        .setMode(HttpMultipartMode.RFC6532);
		HttpPost httpPost = new HttpPost(postUrl);
		httpPost.setEntity(multipartEntityBuilder.build());
		httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
		HttpResponse httpResponse = null;
		try {
			httpResponse = httpClient.execute(httpPost);
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
		HttpEntity httpEntity =  httpResponse.getEntity();
		String content = null;
		try {
			content = EntityUtils.toString(httpEntity);
		} catch (ParseException | IOException e) {
			logger.error(e.getMessage(), e);
		}
		return content;
	}
	
	public static CloseableHttpClient sslHttpClient(String keyStorePath, String keyStorePassword, String keyStoreType) {
		SSLConnectionSocketFactory sslcsf = SSLUtil.sslConnectionSocketFactory(keyStorePath, keyStorePassword, keyStoreType);
		return HttpClients.custom().setSSLSocketFactory(sslcsf).build();
	}
	
	/**
	 * 执行POST请求
	 * 
	 * @param url
	 * @param params
	 * @return
	 * @throws IOException
	 * @throws KeyStoreException 
	 */
	public static String post(String url, String requestXML, CloseableHttpClient sslHttpClient) throws IOException {
		if (StringUtils.isEmpty(url) || sslHttpClient == null) {
			logger.error("httpPost error. url: " + url + ", param: " + requestXML + ", httpClient: " + sslHttpClient);
			return null;
		}
		
		return post(url, "UTF-8", "application/x-www-form-urlencoded", requestXML, sslHttpClient);
	}
	
	
	private static String post(String url, String charset, String contentType, String request, CloseableHttpClient httpClient) throws IOException {
		HttpPost httpPost = new HttpPost(url);
		if (request != null) {
			StringEntity stringEntity = new StringEntity(request, charset);
			stringEntity.setContentType(contentType);
			httpPost.setEntity(stringEntity);
		}
		
		CloseableHttpResponse response = null;
		try {
			response = httpClient.execute(httpPost);
			String res = EntityUtils.toString(response.getEntity(), charset);
			
			if (response.getStatusLine().getStatusCode() == 200) {
				return res;
			}
				
			logger.error("httpPost error. url: " + url + ", param: " + request + ", res: " + res + ", statusCode: " + response.getStatusLine().getStatusCode());
		} catch(Exception e) {
			logger.error("httpPost error. url: " + url + ", param: " + request, e);
			return null;
		} finally {
			if (response != null) {
				response.close();
			}
		}
		
		return null;
	}
}
